/*
 * Copyright 2008 echo.khannedy@gmail.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * ["Let's ROCK with JAVA"]
 * 
 * ["echo.khannedy@gmail.com"]
 * ["http://eecchhoo.wordpress.com/"]
 */
package open.usu.swing;

import java.awt.AlphaComposite;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LayoutManager;
import java.awt.Paint;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import open.usu.swing.event.ChildContainerEvent;
import open.usu.swing.event.ChildContainerListener;

/**
 * EContainer merupakan komponen yang digunakan untuk menampilkan komponen misalkan 
 * menampilkan JPanel, dan pada saat ditampilkan EContainer akan memblock seluruh 
 * aksi untuk Mouse dan KeyBoard sehingga user hanya bisa berinteraksi dengan komponen
 * yang sedang ditampilkan
 * 
 * @author usu
 * @author <a href="mailto:echo.khannedy@gmail.com">echo.khannedy@gmail.com</a>
 * @see <a href="http://eecchhoo.wordpress.com/">http://eecchhoo.wordpress.com/</a>
 * @see <a href="http://biosmansa.wordpress.com/">http://biosmansa.wordpress.com/</a>
 * @see <a href="http://code.google.com/p/eglasspane/">http://code.google.com/p/eglasspane/</a>
 */
public class EContainer extends JPanel {

    // <editor-fold defaultstate="collapsed" desc="Variable Code">
    private static final long serialVersionUID = 730339334905769036L;
    /**
     * properti untuk color block
     */
    public static final String PROPERTY_COLOR_BLOCK = "PROPERTY_COLOR_BLOCK";
    /**
     * properti untuk alpha block
     */
    public static final String PROPERTY_ALPHA_BLOCK = "PROPERTY_ALPHA_BLOCK";
    private BlockListener blockListener;
    private boolean componentShown;
    private int alphaBlock;
    private Paint colorBlock;
    private AlphaComposite alphaComposite;
    private ChildContainerEvent eventChildContainer;
// </editor-fold>
    /**
     * Membuat EContainer baru
     */
    public EContainer() {
        // memanggil superclass
        super();

        // mengubah opaque manjadi false
        super.setOpaque(false);

        // mengubah layout
        super.setLayout(new FlowLayout(FlowLayout.CENTER));

        // mengubah border
        setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

        // mengubah warna block
        setColorBlock(getBackground());

        // menambah listener agar otomatis revalidate
        addComponentListener(new RevalidateListener(this));
    }

    private boolean isComponentShown() {
        return componentShown;
    }

    private void setComponentShown(boolean componentShown) {
        this.componentShown = componentShown;

        // ulangi penggambaran
        repaint();
    }

    /**
     * mendapatkan nilai alpha block
     * @return int nilai alpha
     */
    public int getAlphaBlock() {
        return alphaBlock;
    }

    /**
     * mengubah nilai alpha untuk gambar block, jika nilai baru kurang dari 0
     * atau lebih dari 100, maka akan dirubah menjadi 50
     * @param alphaBlock nilai baru
     */
    public void setAlphaBlock(int alphaBlock) {

        // apakah nilai alpha kurang dari 0 atau lebih dari 100
        if (alphaBlock < 0 || alphaBlock > 100) {
            // jika ya, maka dirubah menjadi 50
            alphaBlock = 50;
        }

        // mendapatkan nilai alpha lama
        int oldValue = getAlphaBlock();

        // mengubah nilai alpha
        this.alphaBlock = alphaBlock;

        // apakah alpha lama tidak sama dengan alpha baru
        if (oldValue != alphaBlock) {
            // memberi tahu propertyChangeListener bahwa alphablock berubah
            firePropertyChange(PROPERTY_ALPHA_BLOCK, oldValue, alphaBlock);
        }

        // ulangi penggambaran
        repaint();
    }

    /**
     * mendapatkan warna block
     * @return Paint 
     */
    public Paint getColorBlock() {
        return colorBlock;
    }

    /**
     * mengubah warna untuk block
     * @param colorBlock
     */
    public void setColorBlock(Paint colorBlock) {
        // mendapatkan colorblock lama
        Paint oldValue = getColorBlock();

        // mengubah colorblock lama
        this.colorBlock = colorBlock;

        // apakah warna lama tidak sama dengan warna baru
        if (oldValue != colorBlock) {
            // memberitahu propertyChangeListeter bahwa colodblock berubah
            firePropertyChange(PROPERTY_COLOR_BLOCK, oldValue, colorBlock);
        }

        // ulangi penggambaran
        repaint();
    }

    /**
     * Menampilkan komponen
     * @param comp komponen yang akan ditampilkan
     */
    public void showComponent(Component comp) {
        // Menghapus semua komponen
        removeAll();

        // memberitahukan bahwa komponen tampil
        setComponentShown(true);

        // Menambahkan komponen
        super.addImpl(comp, null, 0);

        // Meminta agar komponen diberi focus
        comp.requestFocusInWindow();

        // memberitahu bahwa komponen telah muncul/tampil
        fireChildContainerListenerOnChildShow(getEventChildContainer());

        // menambah aksi block
        addMouseListener(getBlockListener());
        addMouseMotionListener(getBlockListener());
        addMouseWheelListener(getBlockListener());
        addKeyListener(getBlockListener());

        // mengubah struktur layout
        doLayout();
        revalidate();
    }

    /**
     * Menghilangkan komponen
     */
    public void hideComponent() {
        // memberitahu jika komponen telah hilang
        fireChildContainerListenerOnChildHide(getEventChildContainer());

        // menghilangkan semua komponen
        removeAll();

        // menghilangkan block
        setComponentShown(false);

        // menghilangkan aksi block
        removeMouseListener(getBlockListener());
        removeMouseMotionListener(getBlockListener());
        removeMouseWheelListener(getBlockListener());
        removeKeyListener(getBlockListener());
    }

    @Override
    protected void paintComponent(Graphics g) {
        // memanggil paintComponent() milik superclass
        super.paintComponent(g);

        // mengecek apakah ada komponen dan warna block != null
        if (isComponentShown() && getColorBlock() != null) {
            paintBlock(g);
        }
    }

    /**
     * Menggambar block
     * @param g
     */
    protected void paintBlock(Graphics g) {
        Graphics2D g2 = (Graphics2D) g.create();
        try {
            // Merubah alpha composite
            alphaComposite = getAlphaComposite().derive((getAlphaBlock() / 100F));

            // merubah nilai Graphics2D
            g2.setComposite(getAlphaComposite());
            g2.setPaint(getColorBlock());

            // menggambar block
            g2.fillRect(0, 0, getWidth(), getHeight());
        } finally {
            // menghilangkan g2
            g2.dispose();
        }
    }
    // <editor-fold defaultstate="collapsed" desc="Temp Code">
    /**
     * Mendapatkan alpha composite
     * @return alpha composite
     */
    protected AlphaComposite getAlphaComposite() {
        // mengecek apakah alpha composite bernilai null
        if (alphaComposite == null) {
            // jika null deklarasikan nilai baru
            alphaComposite = AlphaComposite.SrcOver;
        }
        return alphaComposite;
    }

    /**
     * Mendapatkan block listener
     * @return BlockListener
     */
    public BlockListener getBlockListener() {
        // mengecek apakah block listener bernilai null
        if (blockListener == null) {
            // jika null deklarasinan dengan nilai baru
            blockListener = new BlockListener();
        }
        return blockListener;
    }
    // </editor-fold>
    // <editor-fold defaultstate="collapsed" desc="Deprecated Code">
    @Override
    @Deprecated
    public void setLayout(LayoutManager mgr) {
        // layout tak bisa berubah, hanya bisa FlowLayout
        super.setLayout(new FlowLayout(FlowLayout.CENTER));
    }

    @Override
    @Deprecated
    protected void addImpl(Component comp, Object constraints, int index) {
        return;
    }

    @Override
    @Deprecated
    public void setOpaque(boolean isOpaque) {
        super.setOpaque(false);
    }
    // </editor-fold>
    // <editor-fold defaultstate="collapsed" desc="Action for EContainer">
    /**
     * Mendapatkan event untuk {@link ChildContainerListener}
     * @return {@link ChildContainerEvent}
     */
    private ChildContainerEvent getEventChildContainer() {
        if (eventChildContainer == null) {
            eventChildContainer = new ChildContainerEvent(this);
        }
        return eventChildContainer;
    }

    /**
     * Menambah listener {@link ChildContainerListener}
     * @param listener yang ditambahkan
     */
    public void addChildContainerListener(ChildContainerListener listener) {
        listenerList.add(ChildContainerListener.class, listener);
    }

    /**
     * Menghapus listener {@link ChildContainerListener}
     * @param listener yang akan dihapus
     */
    public void removeChildContainerListener(ChildContainerListener listener) {
        listenerList.remove(ChildContainerListener.class, listener);
    }

    /**
     * menjalankan aksi {@link ChildContainerListener#onChildShow(open.usu.swing.event.ChildContainerEvent) }
     * @param event
     */
    protected void fireChildContainerListenerOnChildShow(ChildContainerEvent event) {
        for (ChildContainerListener listener : listenerList.getListeners(ChildContainerListener.class)) {
            listener.onChildShow(event);
        }
    }

    /**
     * Menjalankan aksi {@link ChildContainerListener#onChildHide(open.usu.swing.event.ChildContainerEvent) }
     * @param event
     */
    protected void fireChildContainerListenerOnChildHide(ChildContainerEvent event) {
        for (ChildContainerListener listener : listenerList.getListeners(ChildContainerListener.class)) {
            listener.onChildHide(event);
        }
    }
// </editor-fold>
    // <editor-fold defaultstate="collapsed" desc="Inner Class">
    /**
     * Listener untuk melakukan proses revalidate()
     * 
     * @author usu
     * @author <a href="mailto:echo.khannedy@gmail.com">echo.khannedy@gmail.com</a>
     * @see <a href="http://eecchhoo.wordpress.com/">http://eecchhoo.wordpress.com/</a>
     * @see <a href="http://biosmansa.wordpress.com/">http://biosmansa.wordpress.com/</a>
     * @see <a href="http://code.google.com/p/eglasspane/">http://code.google.com/p/eglasspane/</a>
     */
    public static class RevalidateListener implements ComponentListener {

        private EContainer container;

        /**
         * Membuat RevalidateListener baru
         * @param container
         */
        public RevalidateListener(EContainer container) {
            this.container = container;
        }

        @Override
        public void componentResized(ComponentEvent e) {
            container.doLayout();
            container.revalidate();
        }

        @Override
        public void componentMoved(ComponentEvent e) {
            componentResized(e);
        }

        @Override
        public void componentShown(ComponentEvent e) {
            componentResized(e);
        }

        @Override
        public void componentHidden(ComponentEvent e) {
            componentResized(e);
        }
    }

    /**
     * Listener untuk memblok seluruh input user
     * 
     * @author usu
     * @author <a href="mailto:echo.khannedy@gmail.com">echo.khannedy@gmail.com</a>
     * @see <a href="http://eecchhoo.wordpress.com/">http://eecchhoo.wordpress.com/</a>
     * @see <a href="http://biosmansa.wordpress.com/">http://biosmansa.wordpress.com/</a>
     * @see <a href="http://code.google.com/p/eglasspane/">http://code.google.com/p/eglasspane/</a>
     */
    public static class BlockListener implements MouseListener, MouseMotionListener,
            MouseWheelListener, KeyListener {

        @Override
        public void mouseClicked(MouseEvent e) {
            // BLOCK
        }

        @Override
        public void mousePressed(MouseEvent e) {
            // BLOCK
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            // BLOCK
        }

        @Override
        public void mouseEntered(MouseEvent e) {
            // BLOCK
        }

        @Override
        public void mouseExited(MouseEvent e) {
            // BLOCK
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            // BLOCK
        }

        @Override
        public void mouseMoved(MouseEvent e) {
            // BLOCK
        }

        @Override
        public void mouseWheelMoved(MouseWheelEvent e) {
            // BLOCK
        }

        @Override
        public void keyTyped(KeyEvent e) {
            // BLOCK
        }

        @Override
        public void keyPressed(KeyEvent e) {
            // BLOCK
        }

        @Override
        public void keyReleased(KeyEvent e) {
            // BLOCK
        }
    }
// </editor-fold>
}
